/**
 * @license Highcharts JS v8.0.0 (2019-12-10)
 *
 * Old IE (v6, v7, v8) module for Highcharts v6+.
 *
 * (c) 2010-2019 Highsoft AS
 * Author: Torstein Honsi
 *
 * License: www.highcharts.com/license
 */
"use strict";
(function (factory) {
  if (typeof module === "object" && module.exports) {
    factory["default"] = factory;
    module.exports = factory;
  } else if (typeof define === "function" && define.amd) {
    define("highcharts/modules/oldie", ["highcharts"], function (Highcharts) {
      factory(Highcharts);
      factory.Highcharts = Highcharts;
      return factory;
    });
  } else {
    factory(typeof Highcharts !== "undefined" ? Highcharts : undefined);
  }
})(function (Highcharts) {
  var _modules = Highcharts ? Highcharts._modules : {};
  function _registerModule(obj, path, args, fn) {
    if (!obj.hasOwnProperty(path)) {
      obj[path] = fn.apply(null, args);
    }
  }
  _registerModule(
    _modules,
    "modules/oldie.src.js",
    [_modules["parts/Globals.js"], _modules["parts/Utilities.js"]],
    function (H, U) {
      /* *
       *
       *  (c) 2010-2019 Torstein Honsi
       *
       *  License: www.highcharts.com/license
       *
       *  Support for old IE browsers (6, 7 and 8) in Highcharts v6+.
       *
       *  !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
       *
       * */
      var defined = U.defined,
        discardElement = U.discardElement,
        erase = U.erase,
        extend = U.extend,
        extendClass = U.extendClass,
        isArray = U.isArray,
        isNumber = U.isNumber,
        isObject = U.isObject,
        offset = U.offset,
        pick = U.pick,
        pInt = U.pInt;
      var VMLRenderer,
        VMLRendererExtension,
        VMLElement,
        Chart = H.Chart,
        createElement = H.createElement,
        css = H.css,
        deg2rad = H.deg2rad,
        doc = H.doc,
        merge = H.merge,
        noop = H.noop,
        svg = H.svg,
        SVGElement = H.SVGElement,
        SVGRenderer = H.SVGRenderer,
        win = H.win;
      /**
       * Path to the pattern image required by VML browsers in order to
       * draw radial gradients.
       *
       * @type      {string}
       * @default   http://code.highcharts.com/{version}/gfx/vml-radial-gradient.png
       * @since     2.3.0
       * @requires  modules/oldie
       * @apioption global.VMLRadialGradientURL
       */
      H.getOptions().global.VMLRadialGradientURL =
        "http://code.highcharts.com/8.0.0/gfx/vml-radial-gradient.png";
      // Utilites
      if (doc && !doc.defaultView) {
        H.getStyle = function (el, prop) {
          var val,
            alias = {
              width: "clientWidth",
              height: "clientHeight",
            }[prop];
          if (el.style[prop]) {
            return pInt(el.style[prop]);
          }
          if (prop === "opacity") {
            prop = "filter";
          }
          // Getting the rendered width and height
          if (alias) {
            el.style.zoom = 1;
            return Math.max(el[alias] - 2 * H.getStyle(el, "padding"), 0);
          }
          val =
            el.currentStyle[
              prop.replace(/\-(\w)/g, function (a, b) {
                return b.toUpperCase();
              })
            ];
          if (prop === "filter") {
            val = val.replace(/alpha\(opacity=([0-9]+)\)/, function (a, b) {
              return b / 100;
            });
          }
          return val === "" ? 1 : pInt(val);
        };
      }
      /* eslint-disable no-invalid-this, valid-jsdoc */
      if (!svg) {
        // Prevent wrapping from creating false offsetWidths in export in legacy IE.
        // This applies only to charts for export, where IE runs the SVGRenderer
        // instead of the VMLRenderer
        // (#1079, #1063)
        H.addEvent(SVGElement, "afterInit", function () {
          if (this.element.nodeName === "text") {
            this.css({
              position: "absolute",
            });
          }
        });
        /**
         * Old IE override for pointer normalize, adds chartX and chartY to event
         * arguments.
         *
         * @ignore
         * @function Highcharts.Pointer#normalize
         * @param {global.PointerEvent} e
         * @param {boolean} [chartPosition=false]
         * @return {Highcharts.PointerEventObject}
         */
        H.Pointer.prototype.normalize = function (e, chartPosition) {
          e = e || win.event;
          if (!e.target) {
            e.target = e.srcElement;
          }
          // Get mouse position
          if (!chartPosition) {
            this.chartPosition = chartPosition = offset(this.chart.container);
          }
          return extend(e, {
            // #2005, #2129: the second case is for IE10 quirks mode within
            // framesets
            chartX: Math.round(Math.max(e.x, e.clientX - chartPosition.left)),
            chartY: Math.round(e.y),
          });
        };
        /**
         * Further sanitize the mock-SVG that is generated when exporting charts in
         * oldIE.
         *
         * @private
         * @function Highcharts.Chart#ieSanitizeSVG
         */
        Chart.prototype.ieSanitizeSVG = function (svg) {
          svg = svg
            .replace(/<IMG /g, "<image ")
            .replace(/<(\/?)TITLE>/g, "<$1title>")
            .replace(/height=([^" ]+)/g, 'height="$1"')
            .replace(/width=([^" ]+)/g, 'width="$1"')
            .replace(/hc-svg-href="([^"]+)">/g, 'xlink:href="$1"/>')
            .replace(/ id=([^" >]+)/g, ' id="$1"') // #4003
            .replace(/class=([^" >]+)/g, 'class="$1"')
            .replace(/ transform /g, " ")
            .replace(/:(path|rect)/g, "$1")
            .replace(/style="([^"]+)"/g, function (s) {
              return s.toLowerCase();
            });
          return svg;
        };
        /**
         * VML namespaces can't be added until after complete. Listening
         * for Perini's doScroll hack is not enough.
         *
         * @private
         * @function Highcharts.Chart#isReadyToRender
         */
        Chart.prototype.isReadyToRender = function () {
          var chart = this;
          // Note: win == win.top is required
          if (
            !svg &&
            win == win.top && // eslint-disable-line eqeqeq
            doc.readyState !== "complete"
          ) {
            doc.attachEvent("onreadystatechange", function () {
              doc.detachEvent("onreadystatechange", chart.firstRender);
              if (doc.readyState === "complete") {
                chart.firstRender();
              }
            });
            return false;
          }
          return true;
        };
        // IE compatibility hack for generating SVG content that it doesn't really
        // understand. Used by the exporting module.
        if (!doc.createElementNS) {
          doc.createElementNS = function (ns, tagName) {
            return doc.createElement(tagName);
          };
        }
        /**
         * Old IE polyfill for addEventListener, called from inside the addEvent
         * function.
         *
         * @private
         * @function Highcharts.addEventListenerPolyfill<T>
         * @param {string} type
         * @param {Highcharts.EventCallbackFunction<T>} fn
         * @return {void}
         */
        H.addEventListenerPolyfill = function (type, fn) {
          var el = this;
          /**
           * @private
           */
          function wrappedFn(e) {
            e.target = e.srcElement || win; // #2820
            fn.call(el, e);
          }
          if (el.attachEvent) {
            if (!el.hcEventsIE) {
              el.hcEventsIE = {};
            }
            // unique function string (#6746)
            if (!fn.hcKey) {
              fn.hcKey = H.uniqueKey();
            }
            // Link wrapped fn with original fn, so we can get this in
            // removeEvent
            el.hcEventsIE[fn.hcKey] = wrappedFn;
            el.attachEvent("on" + type, wrappedFn);
          }
        };
        /**
         * @private
         * @function Highcharts.removeEventListenerPolyfill<T>
         * @param {string} type
         * @param {Highcharts.EventCallbackFunction<T>} fn
         * @return {void}
         */
        H.removeEventListenerPolyfill = function (type, fn) {
          if (this.detachEvent) {
            fn = this.hcEventsIE[fn.hcKey];
            this.detachEvent("on" + type, fn);
          }
        };
        /**
         * The VML element wrapper.
         *
         * @private
         * @class
         * @name Highcharts.VMLElement
         *
         * @augments Highcharts.SVGElement
         */
        VMLElement = {
          docMode8: doc && doc.documentMode === 8,
          /**
           * Initialize a new VML element wrapper. It builds the markup as a
           * string to minimize DOM traffic.
           *
           * @function Highcharts.VMLElement#init
           * @param {Highcharts.VMLRenderer} renderer
           * @param {string} nodeName
           */
          init: function (renderer, nodeName) {
            var wrapper = this,
              markup = ["<", nodeName, ' filled="f" stroked="f"'],
              style = ["position: ", "absolute", ";"],
              isDiv = nodeName === "div";
            // divs and shapes need size
            if (nodeName === "shape" || isDiv) {
              style.push("left:0;top:0;width:1px;height:1px;");
            }
            style.push("visibility: ", isDiv ? "hidden" : "visible");
            markup.push(' style="', style.join(""), '"/>');
            // create element with default attributes and style
            if (nodeName) {
              markup =
                isDiv || nodeName === "span" || nodeName === "img"
                  ? markup.join("")
                  : renderer.prepVML(markup);
              wrapper.element = createElement(markup);
            }
            wrapper.renderer = renderer;
          },
          /**
           * Add the node to the given parent
           *
           * @function Highcharts.VMLElement
           * @param {Highcharts.VMLElement} parent
           * @return {Highcharts.VMLElement}
           */
          add: function (parent) {
            var wrapper = this,
              renderer = wrapper.renderer,
              element = wrapper.element,
              box = renderer.box,
              inverted = parent && parent.inverted,
              // get the parent node
              parentNode = parent ? parent.element || parent : box;
            if (parent) {
              this.parentGroup = parent;
            }
            // if the parent group is inverted, apply inversion on all children
            if (inverted) {
              // only on groups
              renderer.invertChild(element, parentNode);
            }
            // append it
            parentNode.appendChild(element);
            // align text after adding to be able to read offset
            wrapper.added = true;
            if (wrapper.alignOnAdd && !wrapper.deferUpdateTransform) {
              wrapper.updateTransform();
            }
            // fire an event for internal hooks
            if (wrapper.onAdd) {
              wrapper.onAdd();
            }
            // IE8 Standards can't set the class name before the element is
            // appended
            if (this.className) {
              this.attr("class", this.className);
            }
            return wrapper;
          },
          /**
           * VML always uses htmlUpdateTransform
           *
           * @function Highcharts.VMLElement#updateTransform
           */
          updateTransform: SVGElement.prototype.htmlUpdateTransform,
          /**
           * Set the rotation of a span with oldIE's filter
           *
           * @function Highcharts.VMLElement#setSpanRotation
           * @return {void}
           */
          setSpanRotation: function () {
            // Adjust for alignment and rotation. Rotation of useHTML content is
            // not yet implemented but it can probably be implemented for
            // Firefox 3.5+ on user request. FF3.5+ has support for CSS3
            // transform. The getBBox method also needs to be updated to
            // compensate for the rotation, like it currently does for SVG.
            // Test case: https://jsfiddle.net/highcharts/Ybt44/
            var rotation = this.rotation,
              costheta = Math.cos(rotation * deg2rad),
              sintheta = Math.sin(rotation * deg2rad);
            css(this.element, {
              filter: rotation
                ? [
                    "progid:DXImageTransform.Microsoft.Matrix(M11=",
                    costheta,
                    ", M12=",
                    -sintheta,
                    ", M21=",
                    sintheta,
                    ", M22=",
                    costheta,
                    ", sizingMethod='auto expand')",
                  ].join("")
                : "none",
            });
          },
          /**
           * Get the positioning correction for the span after rotating.
           *
           * @function Highcharts.VMLElement#getSpanCorrection
           */
          getSpanCorrection: function (
            width,
            baseline,
            alignCorrection,
            rotation,
            align
          ) {
            var costheta = rotation ? Math.cos(rotation * deg2rad) : 1,
              sintheta = rotation ? Math.sin(rotation * deg2rad) : 0,
              height = pick(this.elemHeight, this.element.offsetHeight),
              quad,
              nonLeft = align && align !== "left";
            // correct x and y
            this.xCorr = costheta < 0 && -width;
            this.yCorr = sintheta < 0 && -height;
            // correct for baseline and corners spilling out after rotation
            quad = costheta * sintheta < 0;
            this.xCorr +=
              sintheta *
              baseline *
              (quad ? 1 - alignCorrection : alignCorrection);
            this.yCorr -=
              costheta *
              baseline *
              (rotation ? (quad ? alignCorrection : 1 - alignCorrection) : 1);
            // correct for the length/height of the text
            if (nonLeft) {
              this.xCorr -= width * alignCorrection * (costheta < 0 ? -1 : 1);
              if (rotation) {
                this.yCorr -=
                  height * alignCorrection * (sintheta < 0 ? -1 : 1);
              }
              css(this.element, {
                textAlign: align,
              });
            }
          },
          /**
           * Converts a subset of an SVG path definition to its VML counterpart.
           * Takes an array as the parameter and returns a string.
           *
           * @function Highcharts.VMLElement#pathToVML
           */
          pathToVML: function (value) {
            // convert paths
            var i = value.length,
              path = [];
            while (i--) {
              // Multiply by 10 to allow subpixel precision.
              // Substracting half a pixel seems to make the coordinates
              // align with SVG, but this hasn't been tested thoroughly
              if (isNumber(value[i])) {
                path[i] = Math.round(value[i] * 10) - 5;
              } else if (value[i] === "Z") {
                // close the path
                path[i] = "x";
              } else {
                path[i] = value[i];
                // When the start X and end X coordinates of an arc are too
                // close, they are rounded to the same value above. In this
                // case, substract or add 1 from the end X and Y positions.
                // #186, #760, #1371, #1410.
                if (value.isArc && (value[i] === "wa" || value[i] === "at")) {
                  // Start and end X
                  if (path[i + 5] === path[i + 7]) {
                    path[i + 7] += value[i + 7] > value[i + 5] ? 1 : -1;
                  }
                  // Start and end Y
                  if (path[i + 6] === path[i + 8]) {
                    path[i + 8] += value[i + 8] > value[i + 6] ? 1 : -1;
                  }
                }
              }
            }
            return path.join(" ") || "x";
          },
          /**
           * Set the element's clipping to a predefined rectangle
           *
           * @function Highcharts.VMLElement#clip
           * @param {Highcharts.VMLClipRectObject} clipRect
           * @return {Highcharts.VMLElement}
           */
          clip: function (clipRect) {
            var wrapper = this,
              clipMembers,
              cssRet;
            if (clipRect) {
              clipMembers = clipRect.members;
              // Ensure unique list of elements (#1258)
              erase(clipMembers, wrapper);
              clipMembers.push(wrapper);
              wrapper.destroyClip = function () {
                erase(clipMembers, wrapper);
              };
              cssRet = clipRect.getCSS(wrapper);
            } else {
              if (wrapper.destroyClip) {
                wrapper.destroyClip();
              }
              cssRet = {
                clip: wrapper.docMode8 ? "inherit" : "rect(auto)",
              }; // #1214
            }
            return wrapper.css(cssRet);
          },
          /**
           * Set styles for the element
           *
           * @function Highcharts.VMLElement#css
           * @param {Highcharts.CSSObject} styles
           * @return {Highcharts.VMLElement}
           */
          css: SVGElement.prototype.htmlCss,
          /**
           * Removes a child either by removeChild or move to garbageBin.
           * Issue 490; in VML removeChild results in Orphaned nodes according to
           * sIEve, discardElement does not.
           *
           * @function Highcharts.VMLElement#safeRemoveChild
           * @param {Highcharts.HTMLDOMElement} element
           * @return {void}
           */
          safeRemoveChild: function (element) {
            // discardElement will detach the node from its parent before
            // attaching it to the garbage bin. Therefore it is important that
            // the node is attached and have parent.
            if (element.parentNode) {
              discardElement(element);
            }
          },
          /**
           * Extend element.destroy by removing it from the clip members array
           *
           * @function Highcharts.VMLElement#destroy
           */
          destroy: function () {
            if (this.destroyClip) {
              this.destroyClip();
            }
            return SVGElement.prototype.destroy.apply(this);
          },
          /**
           * Add an event listener. VML override for normalizing event parameters.
           *
           * @function Highcharts.VMLElement#on
           * @param {string} eventType
           * @param {Function} handler
           * @return {Highcharts.VMLElement}
           */
          on: function (eventType, handler) {
            // simplest possible event model for internal use
            this.element["on" + eventType] = function () {
              var evt = win.event;
              evt.target = evt.srcElement;
              handler(evt);
            };
            return this;
          },
          /**
           * In stacked columns, cut off the shadows so that they don't overlap
           *
           * @function Highcharts.VMLElement#cutOffPath
           * @param {string} path
           * @param {number} length
           * @return {string}
           */
          cutOffPath: function (path, length) {
            var len;
            // The extra comma tricks the trailing comma remover in
            // "gulp scripts" task
            path = path.split(/[ ,]/);
            len = path.length;
            if (len === 9 || len === 11) {
              path[len - 4] = path[len - 2] = pInt(path[len - 2]) - 10 * length;
            }
            return path.join(" ");
          },
          /**
           * Apply a drop shadow by copying elements and giving them different
           * strokes.
           *
           * @function Highcharts.VMLElement#shadow
           * @param {Highcharts.ShadowOptionsObject} shadowOptions
           * @param {Highcharts.VMLElement} group
           * @param {boolean} cutOff
           * @return {Highcharts.VMLElement}
           */
          shadow: function (shadowOptions, group, cutOff) {
            var shadows = [],
              i,
              element = this.element,
              renderer = this.renderer,
              shadow,
              elemStyle = element.style,
              markup,
              path = element.path,
              strokeWidth,
              modifiedPath,
              shadowWidth,
              shadowElementOpacity;
            // some times empty paths are not strings
            if (path && typeof path.value !== "string") {
              path = "x";
            }
            modifiedPath = path;
            if (shadowOptions) {
              shadowWidth = pick(shadowOptions.width, 3);
              shadowElementOpacity =
                (shadowOptions.opacity || 0.15) / shadowWidth;
              for (i = 1; i <= 3; i++) {
                strokeWidth = shadowWidth * 2 + 1 - 2 * i;
                // Cut off shadows for stacked column items
                if (cutOff) {
                  modifiedPath = this.cutOffPath(path.value, strokeWidth + 0.5);
                }
                markup = [
                  '<shape isShadow="true" strokeweight="',
                  strokeWidth,
                  '" filled="false" path="',
                  modifiedPath,
                  '" coordsize="10 10" style="',
                  element.style.cssText,
                  '" />',
                ];
                shadow = createElement(renderer.prepVML(markup), null, {
                  left: pInt(elemStyle.left) + pick(shadowOptions.offsetX, 1),
                  top: pInt(elemStyle.top) + pick(shadowOptions.offsetY, 1),
                });
                if (cutOff) {
                  shadow.cutOff = strokeWidth + 1;
                }
                // apply the opacity
                markup = [
                  '<stroke color="',
                  shadowOptions.color || "#000000",
                  '" opacity="',
                  shadowElementOpacity * i,
                  '"/>',
                ];
                createElement(renderer.prepVML(markup), null, null, shadow);
                // insert it
                if (group) {
                  group.element.appendChild(shadow);
                } else {
                  element.parentNode.insertBefore(shadow, element);
                }
                // record it
                shadows.push(shadow);
              }
              this.shadows = shadows;
            }
            return this;
          },
          updateShadows: noop,
          setAttr: function (key, value) {
            if (this.docMode8) {
              // IE8 setAttribute bug
              this.element[key] = value;
            } else {
              this.element.setAttribute(key, value);
            }
          },
          getAttr: function (key) {
            if (this.docMode8) {
              // IE8 setAttribute bug
              return this.element[key];
            }
            return this.element.getAttribute(key);
          },
          classSetter: function (value) {
            // IE8 Standards mode has problems retrieving the className unless
            // set like this. IE8 Standards can't set the class name before the
            // element is appended.
            (this.added ? this.element : this).className = value;
          },
          dashstyleSetter: function (value, key, element) {
            var strokeElem =
              element.getElementsByTagName("stroke")[0] ||
              createElement(
                this.renderer.prepVML(["<stroke/>"]),
                null,
                null,
                element
              );
            strokeElem[key] = value || "solid";
            // Because changing stroke-width will change the dash length and
            // cause an epileptic effect
            this[key] = value;
          },
          dSetter: function (value, key, element) {
            var i,
              shadows = this.shadows;
            value = value || [];
            // Used in getter for animation
            this.d = value.join && value.join(" ");
            element.path = value = this.pathToVML(value);
            // update shadows
            if (shadows) {
              i = shadows.length;
              while (i--) {
                shadows[i].path = shadows[i].cutOff
                  ? this.cutOffPath(value, shadows[i].cutOff)
                  : value;
              }
            }
            this.setAttr(key, value);
          },
          fillSetter: function (value, key, element) {
            var nodeName = element.nodeName;
            if (nodeName === "SPAN") {
              // text color
              element.style.color = value;
            } else if (nodeName !== "IMG") {
              // #1336
              element.filled = value !== "none";
              this.setAttr(
                "fillcolor",
                this.renderer.color(value, element, key, this)
              );
            }
          },
          "fill-opacitySetter": function (value, key, element) {
            createElement(
              this.renderer.prepVML([
                "<",
                key.split("-")[0],
                ' opacity="',
                value,
                '"/>',
              ]),
              null,
              null,
              element
            );
          },
          // Don't bother - animation is too slow and filters introduce artifacts
          opacitySetter: noop,
          rotationSetter: function (value, key, element) {
            var style = element.style;
            // style is for #1873:
            this[key] = style[key] = value;
            // Correction for the 1x1 size of the shape container. Used in gauge
            // needles.
            style.left = -Math.round(Math.sin(value * deg2rad) + 1) + "px";
            style.top = Math.round(Math.cos(value * deg2rad)) + "px";
          },
          strokeSetter: function (value, key, element) {
            this.setAttr(
              "strokecolor",
              this.renderer.color(value, element, key, this)
            );
          },
          "stroke-widthSetter": function (value, key, element) {
            element.stroked = !!value; // VML "stroked" attribute
            this[key] = value; // used in getter, issue #113
            if (isNumber(value)) {
              value += "px";
            }
            this.setAttr("strokeweight", value);
          },
          titleSetter: function (value, key) {
            this.setAttr(key, value);
          },
          visibilitySetter: function (value, key, element) {
            // Handle inherited visibility
            if (value === "inherit") {
              value = "visible";
            }
            // Let the shadow follow the main element
            if (this.shadows) {
              this.shadows.forEach(function (shadow) {
                shadow.style[key] = value;
              });
            }
            // Instead of toggling the visibility CSS property, move the div out
            // of the viewport. This works around #61 and #586
            if (element.nodeName === "DIV") {
              value = value === "hidden" ? "-999em" : 0;
              // In order to redraw, IE7 needs the div to be visible when
              // tucked away outside the viewport. So the visibility is
              // actually opposite of the expected value. This applies to the
              // tooltip only.
              if (!this.docMode8) {
                element.style[key] = value ? "visible" : "hidden";
              }
              key = "top";
            }
            element.style[key] = value;
          },
          xSetter: function (value, key, element) {
            this[key] = value; // used in getter
            if (key === "x") {
              key = "left";
            } else if (key === "y") {
              key = "top";
            }
            // clipping rectangle special
            if (this.updateClipping) {
              // the key is now 'left' or 'top' for 'x' and 'y'
              this[key] = value;
              this.updateClipping();
            } else {
              // normal
              element.style[key] = value;
            }
          },
          zIndexSetter: function (value, key, element) {
            element.style[key] = value;
          },
          fillGetter: function () {
            return this.getAttr("fillcolor") || "";
          },
          strokeGetter: function () {
            return this.getAttr("strokecolor") || "";
          },
          // #7850
          classGetter: function () {
            return this.getAttr("className") || "";
          },
        };
        VMLElement["stroke-opacitySetter"] = VMLElement["fill-opacitySetter"];
        H.VMLElement = VMLElement = extendClass(SVGElement, VMLElement);
        // Some shared setters
        VMLElement.prototype.ySetter =
          VMLElement.prototype.widthSetter =
          VMLElement.prototype.heightSetter =
            VMLElement.prototype.xSetter;
        /**
         * The VML renderer
         *
         * @private
         * @class
         * @name Highcharts.VMLRenderer
         *
         * @augments Highcharts.SVGRenderer
         */
        VMLRendererExtension = {
          Element: VMLElement,
          isIE8: win.navigator.userAgent.indexOf("MSIE 8.0") > -1,
          /**
           * Initialize the VMLRenderer.
           *
           * @function Highcharts.VMLRenderer#init
           * @param {Highcharts.HTMLDOMElement} container
           * @param {number} width
           * @param {number} height
           * @return {void}
           */
          init: function (container, width, height) {
            var renderer = this,
              boxWrapper,
              box,
              css;
            renderer.alignedObjects = [];
            boxWrapper = renderer
              .createElement("div")
              .css({ position: "relative" });
            box = boxWrapper.element;
            container.appendChild(boxWrapper.element);
            // generate the containing box
            renderer.isVML = true;
            renderer.box = box;
            renderer.boxWrapper = boxWrapper;
            renderer.gradients = {};
            renderer.cache = {}; // Cache for numerical bounding boxes
            renderer.cacheKeys = [];
            renderer.imgCount = 0;
            renderer.setSize(width, height, false);
            // The only way to make IE6 and IE7 print is to use a global
            // namespace. However, with IE8 the only way to make the dynamic
            // shapes visible in screen and print mode seems to be to add the
            // xmlns attribute and the behaviour style inline.
            if (!doc.namespaces.hcv) {
              doc.namespaces.add("hcv", "urn:schemas-microsoft-com:vml");
              // Setup default CSS (#2153, #2368, #2384)
              css =
                "hcv\\:fill, hcv\\:path, hcv\\:shape, hcv\\:stroke" +
                "{ behavior:url(#default#VML); display: inline-block; } ";
              try {
                doc.createStyleSheet().cssText = css;
              } catch (e) {
                doc.styleSheets[0].cssText += css;
              }
            }
          },
          /**
           * Detect whether the renderer is hidden. This happens when one of the
           * parent elements has display: none
           *
           * @function Highcharts.VMLRenderer#isHidden
           */
          isHidden: function () {
            return !this.box.offsetWidth;
          },
          /**
           * Define a clipping rectangle. In VML it is accomplished by storing the
           * values for setting the CSS style to all associated members.
           *
           * @function Highcharts.VMLRenderer#clipRect
           * @param {number|Highcharts.SizeObject} x
           * @param {number} y
           * @param {number} width
           * @param {number} height
           * @return {Highcharts.VMLElement}
           */
          clipRect: function (x, y, width, height) {
            // create a dummy element
            var clipRect = this.createElement(),
              isObj = isObject(x);
            // mimic a rectangle with its style object for automatic updating in
            // attr
            return extend(clipRect, {
              members: [],
              count: 0,
              left: (isObj ? x.x : x) + 1,
              top: (isObj ? x.y : y) + 1,
              width: (isObj ? x.width : width) - 1,
              height: (isObj ? x.height : height) - 1,
              getCSS: function (wrapper) {
                var element = wrapper.element,
                  nodeName = element.nodeName,
                  isShape = nodeName === "shape",
                  inverted = wrapper.inverted,
                  rect = this,
                  top = rect.top - (isShape ? element.offsetTop : 0),
                  left = rect.left,
                  right = left + rect.width,
                  bottom = top + rect.height,
                  ret = {
                    clip:
                      "rect(" +
                      Math.round(inverted ? left : top) +
                      "px," +
                      Math.round(inverted ? bottom : right) +
                      "px," +
                      Math.round(inverted ? right : bottom) +
                      "px," +
                      Math.round(inverted ? top : left) +
                      "px)",
                  };
                // issue 74 workaround
                if (!inverted && wrapper.docMode8 && nodeName === "DIV") {
                  extend(ret, {
                    width: right + "px",
                    height: bottom + "px",
                  });
                }
                return ret;
              },
              // used in attr and animation to update the clipping of all
              // members
              updateClipping: function () {
                clipRect.members.forEach(function (member) {
                  // Member.element is falsy on deleted series, like in
                  // stock/members/series-remove demo. Should be removed
                  // from members, but this will do.
                  if (member.element) {
                    member.css(clipRect.getCSS(member));
                  }
                });
              },
            });
          },
          /**
           * Take a color and return it if it's a string, make it a gradient if
           * it's a gradient configuration object, and apply opacity.
           *
           * @function Highcharts.VMLRenderer#color<T>
           *
           * @param {T} color
           *        The color or config object
           *
           * @return {T}
           */
          color: function (color, elem, prop, wrapper) {
            var renderer = this,
              colorObject,
              regexRgba = /^rgba/,
              markup,
              fillType,
              ret = "none";
            // Check for linear or radial gradient
            if (color && color.linearGradient) {
              fillType = "gradient";
            } else if (color && color.radialGradient) {
              fillType = "pattern";
            }
            if (fillType) {
              var stopColor,
                stopOpacity,
                gradient = color.linearGradient || color.radialGradient,
                x1,
                y1,
                x2,
                y2,
                opacity1,
                opacity2,
                color1,
                color2,
                fillAttr = "",
                stops = color.stops,
                firstStop,
                lastStop,
                colors = [],
                addFillNode = function () {
                  // Add the fill subnode. When colors attribute is used,
                  // the meanings of opacity and o:opacity2 are reversed.
                  markup = [
                    '<fill colors="' + colors.join(",") + '" opacity="',
                    opacity2,
                    '" o:opacity2="',
                    opacity1,
                    '" type="',
                    fillType,
                    '" ',
                    fillAttr,
                    'focus="100%" method="any" />',
                  ];
                  createElement(renderer.prepVML(markup), null, null, elem);
                };
              // Extend from 0 to 1
              firstStop = stops[0];
              lastStop = stops[stops.length - 1];
              if (firstStop[0] > 0) {
                stops.unshift([0, firstStop[1]]);
              }
              if (lastStop[0] < 1) {
                stops.push([1, lastStop[1]]);
              }
              // Compute the stops
              stops.forEach(function (stop, i) {
                if (regexRgba.test(stop[1])) {
                  colorObject = H.color(stop[1]);
                  stopColor = colorObject.get("rgb");
                  stopOpacity = colorObject.get("a");
                } else {
                  stopColor = stop[1];
                  stopOpacity = 1;
                }
                // Build the color attribute
                colors.push(stop[0] * 100 + "% " + stopColor);
                // Only start and end opacities are allowed, so we use the
                // first and the last
                if (!i) {
                  opacity1 = stopOpacity;
                  color2 = stopColor;
                } else {
                  opacity2 = stopOpacity;
                  color1 = stopColor;
                }
              });
              // Apply the gradient to fills only.
              if (prop === "fill") {
                // Handle linear gradient angle
                if (fillType === "gradient") {
                  x1 = gradient.x1 || gradient[0] || 0;
                  y1 = gradient.y1 || gradient[1] || 0;
                  x2 = gradient.x2 || gradient[2] || 0;
                  y2 = gradient.y2 || gradient[3] || 0;
                  fillAttr =
                    'angle="' +
                    (90 -
                      (Math.atan(
                        (y2 - y1) / // y vector
                          (x2 - x1) // x vector
                      ) *
                        180) /
                        Math.PI) +
                    '"';
                  addFillNode();
                  // Radial (circular) gradient
                } else {
                  var r = gradient.r,
                    sizex = r * 2,
                    sizey = r * 2,
                    cx = gradient.cx,
                    cy = gradient.cy,
                    radialReference = elem.radialReference,
                    bBox,
                    applyRadialGradient = function () {
                      if (radialReference) {
                        bBox = wrapper.getBBox();
                        cx += (radialReference[0] - bBox.x) / bBox.width - 0.5;
                        cy += (radialReference[1] - bBox.y) / bBox.height - 0.5;
                        sizex *= radialReference[2] / bBox.width;
                        sizey *= radialReference[2] / bBox.height;
                      }
                      fillAttr =
                        'src="' +
                        H.getOptions().global.VMLRadialGradientURL +
                        '" ' +
                        'size="' +
                        sizex +
                        "," +
                        sizey +
                        '" ' +
                        'origin="0.5,0.5" ' +
                        'position="' +
                        cx +
                        "," +
                        cy +
                        '" ' +
                        'color2="' +
                        color2 +
                        '" ';
                      addFillNode();
                    };
                  // Apply radial gradient
                  if (wrapper.added) {
                    applyRadialGradient();
                  } else {
                    // We need to know the bounding box to get the size
                    // and position right
                    wrapper.onAdd = applyRadialGradient;
                  }
                  // The fill element's color attribute is broken in IE8
                  // standards mode, so we need to set the parent shape's
                  // fillcolor attribute instead.
                  ret = color1;
                }
                // Gradients are not supported for VML stroke, return the first
                // color. #722.
              } else {
                ret = stopColor;
              }
              // If the color is an rgba color, split it and add a fill node
              // to hold the opacity component
            } else if (regexRgba.test(color) && elem.tagName !== "IMG") {
              colorObject = H.color(color);
              wrapper[prop + "-opacitySetter"](
                colorObject.get("a"),
                prop,
                elem
              );
              ret = colorObject.get("rgb");
            } else {
              // 'stroke' or 'fill' node
              var propNodes = elem.getElementsByTagName(prop);
              if (propNodes.length) {
                propNodes[0].opacity = 1;
                propNodes[0].type = "solid";
              }
              ret = color;
            }
            return ret;
          },
          /**
           * Take a VML string and prepare it for either IE8 or IE6/IE7.
           *
           * @function Highcharts.VMLRenderer#prepVML
           *
           * @param {Array<(number|string)>} markup
           *        A string array of the VML markup to prepare
           *
           * @return {string}
           */
          prepVML: function (markup) {
            var vmlStyle = "display:inline-block;behavior:url(#default#VML);",
              isIE8 = this.isIE8;
            markup = markup.join("");
            if (isIE8) {
              // add xmlns and style inline
              markup = markup.replace(
                "/>",
                ' xmlns="urn:schemas-microsoft-com:vml" />'
              );
              if (markup.indexOf('style="') === -1) {
                markup = markup.replace("/>", ' style="' + vmlStyle + '" />');
              } else {
                markup = markup.replace('style="', 'style="' + vmlStyle);
              }
            } else {
              // add namespace
              markup = markup.replace("<", "<hcv:");
            }
            return markup;
          },
          /**
           * Create rotated and aligned text
           *
           * @function Highcharts.VMLRenderer#text
           *
           * @param {string} str
           *
           * @param {number} x
           *
           * @param {number} y
           */
          text: SVGRenderer.prototype.html,
          /**
           * Create and return a path element
           *
           * @function Highcharts.VMLRenderer#path
           *
           * @param {Highcharts.VMLAttributes|Highcharts.VMLPathArray} [path]
           */
          path: function (path) {
            var attr = {
              // subpixel precision down to 0.1 (width and height = 1px)
              coordsize: "10 10",
            };
            if (isArray(path)) {
              attr.d = path;
            } else if (isObject(path)) {
              // attributes
              extend(attr, path);
            }
            // create the shape
            return this.createElement("shape").attr(attr);
          },
          /**
           * Create and return a circle element. In VML circles are implemented as
           * shapes, which is faster than v:oval
           *
           * @function Highcharts.VMLRenderer#circle
           * @param {number|Highcharts.Dictionary<number>} x
           * @param {number} [y]
           * @param {number} [r]
           * @return {Highcharts.VMLElement}
           */
          circle: function (x, y, r) {
            var circle = this.symbol("circle");
            if (isObject(x)) {
              r = x.r;
              y = x.y;
              x = x.x;
            }
            circle.isCircle = true; // Causes x and y to mean center (#1682)
            circle.r = r;
            return circle.attr({ x: x, y: y });
          },
          /**
           * Create a group using an outer div and an inner v:group to allow
           * rotating and flipping. A simple v:group would have problems with
           * positioning child HTML elements and CSS clip.
           *
           * @function Highcharts.VMLRenderer#g
           *
           * @param {string} name
           *        The name of the group
           *
           * @return {Highcharts.VMLElement}
           */
          g: function (name) {
            var wrapper, attribs;
            // set the class name
            if (name) {
              attribs = {
                className: "highcharts-" + name,
                class: "highcharts-" + name,
              };
            }
            // the div to hold HTML and clipping
            wrapper = this.createElement("div").attr(attribs);
            return wrapper;
          },
          /**
           * VML override to create a regular HTML image.
           *
           * @function Highcharts.VMLRenderer#image
           *
           * @param {string} src
           *
           * @param {number} x
           *
           * @param {number} y
           *
           * @param {number} width
           *
           * @param {number} height
           * @return {Highcharts.VMLElement}
           */
          image: function (src, x, y, width, height) {
            var obj = this.createElement("img").attr({ src: src });
            if (arguments.length > 1) {
              obj.attr({
                x: x,
                y: y,
                width: width,
                height: height,
              });
            }
            return obj;
          },
          /**
           * For rectangles, VML uses a shape for rect to overcome bugs and
           * rotation problems
           *
           * @function Highcharts.VMLRenderer#createElement
           * @param {string} nodeName
           * @return {Highcharts.VMLElement}
           */
          createElement: function (nodeName) {
            return nodeName === "rect"
              ? this.symbol(nodeName)
              : SVGRenderer.prototype.createElement.call(this, nodeName);
          },
          /**
           * In the VML renderer, each child of an inverted div (group) is
           * inverted
           *
           * @function Highcharts.VMLRenderer#invertChild
           *
           * @param {Highcharts.HTMLDOMElement} element
           *
           * @param {Highcharts.HTMLDOMElement} parentNode
           */
          invertChild: function (element, parentNode) {
            var ren = this,
              parentStyle = parentNode.style,
              imgStyle = element.tagName === "IMG" && element.style; // #1111
            css(element, {
              flip: "x",
              left:
                pInt(parentStyle.width) - (imgStyle ? pInt(imgStyle.top) : 1),
              top:
                pInt(parentStyle.height) - (imgStyle ? pInt(imgStyle.left) : 1),
              rotation: -90,
            });
            // Recursively invert child elements, needed for nested composite
            // shapes like box plots and error bars. #1680, #1806.
            [].forEach.call(element.childNodes, function (child) {
              ren.invertChild(child, element);
            });
          },
          /**
           * Symbol definitions that override the parent SVG renderer's symbols
           *
           * @name Highcharts.VMLRenderer#symbols
           * @type {Highcharts.Dictionary<Function>}
           */
          symbols: {
            // VML specific arc function
            arc: function (x, y, w, h, options) {
              var start = options.start,
                end = options.end,
                radius = options.r || w || h,
                innerRadius = options.innerR,
                cosStart = Math.cos(start),
                sinStart = Math.sin(start),
                cosEnd = Math.cos(end),
                sinEnd = Math.sin(end),
                ret;
              if (end - start === 0) {
                // no angle, don't show it.
                return ["x"];
              }
              ret = [
                "wa",
                x - radius,
                y - radius,
                x + radius,
                y + radius,
                x + radius * cosStart,
                y + radius * sinStart,
                x + radius * cosEnd,
                y + radius * sinEnd, // end y
              ];
              if (options.open && !innerRadius) {
                ret.push(
                  "e",
                  "M",
                  x, // - innerRadius,
                  y // - innerRadius
                );
              }
              ret.push(
                "at", // anti clockwise arc to
                x - innerRadius, // left
                y - innerRadius, // top
                x + innerRadius, // right
                y + innerRadius, // bottom
                x + innerRadius * cosEnd, // start x
                y + innerRadius * sinEnd, // start y
                x + innerRadius * cosStart, // end x
                y + innerRadius * sinStart, // end y
                "x", // finish path
                "e" // close
              );
              ret.isArc = true;
              return ret;
            },
            // Add circle symbol path. This performs significantly faster than
            // v:oval.
            circle: function (x, y, w, h, wrapper) {
              if (wrapper && defined(wrapper.r)) {
                w = h = 2 * wrapper.r;
              }
              // Center correction, #1682
              if (wrapper && wrapper.isCircle) {
                x -= w / 2;
                y -= h / 2;
              }
              // Return the path
              return [
                "wa",
                x,
                y,
                x + w,
                y + h,
                x + w,
                y + h / 2,
                x + w,
                y + h / 2,
                "e", // close
              ];
            },
            /**
             * Add rectangle symbol path which eases rotation and omits arcsize
             * problems compared to the built-in VML roundrect shape. When
             * borders are not rounded, use the simpler square path, else use
             * the callout path without the arrow.
             */
            rect: function (x, y, w, h, options) {
              return SVGRenderer.prototype.symbols[
                !defined(options) || !options.r ? "square" : "callout"
              ].call(0, x, y, w, h, options);
            },
          },
        };
        H.VMLRenderer = VMLRenderer = function () {
          this.init.apply(this, arguments);
        };
        VMLRenderer.prototype = merge(
          SVGRenderer.prototype,
          VMLRendererExtension
        );
        // general renderer
        H.Renderer = VMLRenderer;
      }
      SVGRenderer.prototype.getSpanWidth = function (wrapper, tspan) {
        var renderer = this,
          bBox = wrapper.getBBox(true),
          actualWidth = bBox.width;
        // Old IE cannot measure the actualWidth for SVG elements (#2314)
        if (!svg && renderer.forExport) {
          actualWidth = renderer.measureSpanWidth(
            tspan.firstChild.data,
            wrapper.styles
          );
        }
        return actualWidth;
      };
      // This method is used with exporting in old IE, when emulating SVG (see #2314)
      SVGRenderer.prototype.measureSpanWidth = function (text, styles) {
        var measuringSpan = doc.createElement("span"),
          offsetWidth,
          textNode = doc.createTextNode(text);
        measuringSpan.appendChild(textNode);
        css(measuringSpan, styles);
        this.box.appendChild(measuringSpan);
        offsetWidth = measuringSpan.offsetWidth;
        discardElement(measuringSpan); // #2463
        return offsetWidth;
      };
    }
  );
  _registerModule(_modules, "masters/modules/oldie.src.js", [], function () {});
});
